package com.deng.eventflow.core.functions;

import com.deng.eventflow.core.Context;
import com.deng.eventflow.core.EventTracker;

import java.util.Set;

public abstract class BinaryFunction <T, V, U> extends Function {

    private Class<T> m_inputTypeLeft;

    private Class<V> m_inputTypeRight;

    private Class<U> m_outputType;

    public BinaryFunction(Class<T> t, Class<V> v, Class<U> u) {
        super();
        m_inputTypeLeft = t;
        m_inputTypeRight = v;
        m_outputType = u;
    }

    public void evaluate(Object[] inputs, Object[] outputs, Context context, EventTracker tracker) {
        outputs[0] = getValue((T) inputs[0], (V) inputs[1]);
        if (tracker != null)
        {
            trackAssociations((T) inputs[0], (V) inputs[1], (U) outputs[0], tracker);
        }
    }

    public abstract U getValue(T x, V y);

    protected void trackAssociations(T x, V y, U z, EventTracker tracker)
    {
        tracker.associateToInput(-1, 0, 0, 0, 0);
        tracker.associateToInput(-1, 1, 0, 0, 0);
    }

    @Override
    public final int getInputArity()
    {
        return 2;
    }

    @Override
    public final int getOutputArity()
    {
        return 1;
    }

    public U getStartValue()
    {
        return null;
    }

    @Override
    public void reset()
    {
        // Do nothing
    }

    @Override
    public BinaryFunction<T, V, U> duplicate(boolean with_state)
    {
        return this;
    }

    public final Class<T> getInputTypeLeft()
    {
        return m_inputTypeLeft;
    }

    public final Class<V> getInputTypeRight()
    {
        return m_inputTypeRight;
    }

    @Override
    public final void getInputTypesFor(/*@ non_null @*/ Set<Class<?>> classes, int index)
    {
        if (index == 0)
        {
            classes.add(m_inputTypeLeft);
        }
        else
        {
            classes.add(m_inputTypeRight);
        }
    }

    public final Class<U> getOutputType()
    {
        return m_outputType;
    }

    @Override
    public final Class<?> getOutputTypeFor(int index)
    {
        return m_outputType;
    }

}
